/**
 * 
 */
package org.youisoft.summer.evaluator.function.core;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.youisoft.summer.evaluator.InvokeException;
import org.youisoft.summer.evaluator.UnitEnum;
import org.youisoft.summer.evaluator.annotation.Context;
import org.youisoft.summer.evaluator.annotation.Invoke;
import org.youisoft.summer.evaluator.context.CalContext;
import org.youisoft.summer.evaluator.function.Function;
import org.youisoft.summer.evaluator.function.FunctionCallback;
import org.youisoft.summer.evaluator.operator.Operator;
import org.youisoft.summer.evaluator.registry.DefaultFunctionRegistry;
import org.youisoft.summer.evaluator.util.Ulitity;

/**
 * @author zijiang.jl
 *
 */
public class AdapterFunction extends DefaultFunctionRegistry implements Function{
	private Log logger=LogFactory.getLog(AdapterFunction.class);
	private Object obj;
	private java.lang.reflect.Field contextField;
	private java.lang.reflect.Method contextMethod;
	
	//============================
	private java.lang.reflect.Method braceInvoke;
	private java.lang.reflect.Method bracketInvoke;
	private java.lang.reflect.Method parenthesisInvoke;
	private FunctionCallback call;
	private String cName;
	private String[] alias;
	private UnitEnum type=UnitEnum.parenthesis;
	
	public AdapterFunction(Object obj){
		try {
			this.obj=obj;
			Class<?> clazz=obj.getClass();
			org.youisoft.summer.evaluator.annotation.Component com=clazz.getAnnotation(org.youisoft.summer.evaluator.annotation.Component.class);
			if(com!=null){
				this.cName=com.name();
				this.alias=com.alias();
				if(FunctionCallback.class.isAssignableFrom(com.callback())){
					if(!FunctionCallback.class.equals(com.callback())){
						call=com.callback().newInstance();
					}
				}
			}
			
			Field[] fs=clazz.getDeclaredFields();
			for(Field f:fs){
				if(f.getAnnotation(Context.class)!=null) contextField=f;

			}
			
			Method[] ms=clazz.getDeclaredMethods();
			for(Method m:ms){
				if(m.getAnnotation(Context.class)!=null) this.contextMethod=m;
				if(m.getAnnotation(Invoke.class)!=null) {
					UnitEnum type=m.getAnnotation(Invoke.class).type();
					switch(type){
						case brace:
							this.braceInvoke=m;
							break;
						case bracket:
							this.bracketInvoke=m;
							break;
						default:
							this.parenthesisInvoke=m;
					}
				
				}
			}
			
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}

	}
	
	

	@Override
	public boolean addendumOperator() {
		return UnitEnum.bracket.equals(this.type);
	}



	@Override
	public UnitEnum getType() {
		return this.type;
	}



	@Override
	public void setType(UnitEnum type) {
		this.type=type;
		
	}



	@Override
	public boolean isValidate() {
		return this.braceInvoke!=null||this.bracketInvoke!=null||this.parenthesisInvoke!=null;
	}
	
	@Override
	public <C, R, P> boolean isIgnore(CalContext<C, R, P> context) {
		return false;
	}
	
	
	
	@Override
	public String[] getAlias() {
		return this.alias;
	}


	
	@Override
	public String getName() {
		return this.cName;

	}

	@Override
	public <C, R, P> Operator<?> invoke(CalContext<C, R, P> context,
			FunctionCallback callback, Operator<?>... operator)
			throws InvokeException {
		Operator<?> op=null;
		switch(this.getType()){
			case brace:
				op=call.doBraceInvoke(context, operator);
				break;
			case bracket:
				op=call.doBracketInvoke(context, operator);
				break;
			default:
				op=call.doParenthesisInvoke(context, operator);
		}
		return op;
	}

	@Override
	public <C, R, P> Operator<?> invoke(CalContext<C, R, P> context,
			Operator<?>... operator) throws InvokeException {
		this.adapterContext(context);
		return this.adapterInvoke(operator);
	}

	
	private <C, R, P> void adapterContext(CalContext<C, R, P> context){
		try {
			if(context!=null
					&&contextField!=null
					&&Ulitity.translateClass(contextField.getType()).isAssignableFrom(context.getContext().getClass())){
				boolean ac=false;
				if(!contextField.isAccessible()) {
					contextField.setAccessible(true);
					ac=true;
				
				}
				contextField.set(obj, context.getContext());
				if(ac) contextField.setAccessible(false);
			}
			
			
			if(context!=null
					&&this.contextMethod!=null){
				boolean ac=false;
				if(!contextMethod.isAccessible()) {
					contextMethod.setAccessible(true);
					ac=true;
				}
				Class<?>[] paras=this.contextMethod.getParameterTypes();
				if(paras!=null
						&&paras.length>0
						&&Ulitity.translateClass(paras[0]).isAssignableFrom(context.getContext().getClass())){
					contextMethod.invoke(obj, new Object[]{context.getContext()});
				}
				
				if(ac) contextMethod.setAccessible(false);
			}
		} catch (Exception e) {
			logger.error("Adapter Context failuare:"+obj.getClass().getName(), e);
		}
	}


	private Operator<?> doInvoke(java.lang.reflect.Method invokeMethod,Operator<?>... operator){
		if(invokeMethod!=null){
			boolean ac=false;
			if(!invokeMethod.isAccessible()) {
				invokeMethod.setAccessible(true);
				ac=true;
			}
			Class<?>[] pars=invokeMethod.getParameterTypes();
			Object[] par=new Object[pars.length];
			
			for(int i=0;i<pars.length;i++){
//				if(i==0&&pars[i].isArray()){
//					par=new Object[operator.length];
//					for(int j=0;j<operator.length;j++){
//						if(Ulitity.translateClass(pars[i].getComponentType()).isAssignableFrom(operator[j].value().getClass())){
//							par[j]=operator[j].value();
//						}else{
//							par[j]=null;
//						}
//					}
//					
//					break;
//				}else 
				if(Ulitity.translateClass(pars[i]).isAssignableFrom(operator[i].value().getClass())){
					par[i]=operator[i].value();
				}else{
					par[i]=null;
				}
				
			}
			try {
				if(pars.length>0){
					return Ulitity.smartWrapResource(invokeMethod.invoke(obj,par));	
				}else{
					return Ulitity.smartWrapResource(invokeMethod.invoke(obj));
				}
			} catch (Exception e) {
				logger.error("adapter failure:"+this.getName());
				}finally{
				if(ac) invokeMethod.setAccessible(false);
			}
			
		}
		return Operator.nullInstance;
	}
	
	private Operator<?> adapterInvoke(Operator<?>... operator){
		Operator<?> op=null;
		switch(this.getType()){
			case brace:
				op=this.doInvoke(this.braceInvoke, operator);
				break;
			case bracket:
				op=this.doInvoke(this.bracketInvoke, operator);
				break;
			default:
				op=this.doInvoke(this.parenthesisInvoke, operator);
		}
		return op;
	}
	
	
	@Override
	public <C, R, P> boolean supportOperator(CalContext<C, R, P> context,
			Operator<?>... operator) {
		return true;
	}


	public final void setcName(String cName) {
		this.cName = cName;
	}

	@Override
	public void destory() {
		
		
	}


	@Override
	public void init() {
		
		
	}

	@Override
	public void reInit() {
		
		
	}

	@Override
	public void clear() {
		
	}




	@Override
	public boolean isInit() {
		
		return false;
	}

	
	
}
